// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//


#pragma once

#include <cantor/parser/ast/expr.h>

namespace cantor::parser {

    ////////////////////////////////////////////////////////////////////////////////
    /// Data Control Language (DCL) Types and AST Node
    ///
    /// This Enum defines various types of DCL elements such as user identifiers,
    /// privilege specifications, privilege types and levels, and authentication options.
    ///
    /// The DclNode struct is the base AST node for all DCL-related constructs in the parser.
    /// It holds a DCLType to specify which kind of DCL element it represents.
    ///
    /// Usage:
    ///   - Parsing and representing DCL statements in the SQL AST.
    ///   - Derived nodes should extend DclNode for specific DCL constructs.
    ////////////////////////////////////////////////////////////////////////////////


    struct DclNode : public DefiniteExpr {
        DclNode(pb::ast::ExprNodeType t) : DefiniteExpr(t, pb::ast::ENC_DCL) {
        }
        DclNode(pb::ast::ExprNodeType t, pb::ast::ExprNodeClass c) : DefiniteExpr(t, c) {
        }
    };

    ////////////////////////////////////////////////////////////////////////////////
    /// UserIdentity - Represents a user identity consisting of username and hostname.
    ///
    /// This struct models the identity of a database user for access control,
    /// authentication, and privileges management.
    ///
    /// It is typically used in SQL DCL (Data Control Language) statements such as
    /// `CREATE USER`, `ALTER USER`, or privilege grants/revokes.
    ///
    /// Fields:
    ///   - username: The name of the user.
    ///   - hostname: The host or IP from which the user connects (e.g. '%', 'localhost').
    ///   - current_user: A flag to indicate if this identity refers to the current session user.
    ///
    /// Example:
    ///   -- Represents user 'alice' connecting from any host.
    ///   UserIdentity ui;
    ///   ui.username = "alice";
    ///   ui.hostname = "%";
    ///
    ///   -- Represents the current session user.
    ///   UserIdentity current_ui;
    ///   current_ui.current_user = true;
    ////////////////////////////////////////////////////////////////////////////////

    struct UserIdentity : public DclNode {
        UserIdentity() : DclNode(pb::ast::ENT_USER_IDENTIFIER) {
            username = nullptr;
            hostname = nullptr;
        }

        nlohmann::ordered_json to_json() const override;
        /// The user's name.
        String username;

        /// The host from which the user connects.
        /// May include wildcards such as '%' to allow connections from any host.
        String hostname;

        /// Flag indicating if this refers to the current session user,
        /// allowing shorthand usage in SQL statements.
        bool current_user = false;
    };

    ////////////////////////////////////////////////////////////////////////////////
    /// AuthOption - Represents authentication options in DCL statements.
    ///
    /// This node is used to specify authentication details for a user, typically
    /// within GRANT or CREATE USER statements in SQL. It encapsulates the authentication
    /// method, credentials, and related plugin information.
    ///
    /// Fields:
    ///   - auth_string: The main authentication string, typically a password or key.
    ///                  This is the primary credential used for authentication.
    ///   - hash_string: Currently not supported; reserved for future use to store
    ///                  hashed credentials or tokens.
    ///   - auth_plugin: Currently not supported; reserved for specifying authentication
    ///                  plugins (e.g., LDAP, PAM) if applicable.
    ///
    /// Example usage:
    ///   CREATE USER 'alice'@'localhost' IDENTIFIED BY 'mypassword';
    ///
    ///   Here, 'mypassword' would be represented in the auth_string field.
    ///
    /// Note:
    ///   - hash_string and auth_plugin are placeholders for extended authentication
    ///     mechanisms planned for future implementation.
    ////////////////////////////////////////////////////////////////////////////////

    struct AuthOption : public DclNode {
        AuthOption() : DclNode(pb::ast::ENT_AUTH_OPTION) {
            auth_string = nullptr;
            hash_string = nullptr;
            auth_plugin = nullptr;
        }

        nlohmann::ordered_json to_json() const override;

        /// by default: password or credential string
        String auth_string;
        /// not supported yet: placeholder for hashed credentials
        String hash_string;
        /// not supported yet: placeholder for authentication plugin
        String auth_plugin;
    };

    ////////////////////////////////////////////////////////////////////////////////
    /// UserSpec - Represents a user specification in DCL statements.
    ///
    /// This node models the user information along with the associated authentication
    /// options used in statements like CREATE USER, ALTER USER, or GRANT.
    ///
    /// Fields:
    ///   - user: Pointer to a UserIdentity node that holds the username and hostname
    ///           information. This identifies which user is being specified.
    ///   - auth_opt: Pointer to an AuthOption node containing authentication details
    ///               such as password or plugin options for the user.
    ///
    /// Example usage:
    ///   CREATE USER 'bob'@'%' IDENTIFIED BY 'secret';
    ///
    ///   Here, 'bob'@'%' maps to the user field (UserIdentity),
    ///   and 'secret' maps to the auth_opt field (AuthOption).
    ///
    /// Notes:
    ///   - Both fields are pointers and may be nullptr if not specified.
    ///   - This node serves as a container linking user identity with authentication.
    ////////////////////////////////////////////////////////////////////////////////

    struct UserSpec : public DclNode {
        UserSpec() : DclNode(pb::ast::ENT_USER_SPEC) {
        }

        nlohmann::ordered_json to_json() const override;

        /// User's identity (username and hostname)
        UserIdentity *user{nullptr};

        /// Authentication options for the user
        AuthOption *auth_opt{nullptr};
    };

    // https://github.com/mysql/mysql-server/blob/5.6/sql/sql_acl.h
    enum SqlACL: uint32_t {
        SELECT_ACL = (1U << 0), //SELECT  Enable use of SELECT. Levels: Global, database, table, column.
        INSERT_ACL = (1U << 1), //INSERT  Enable use of INSERT. Levels: Global, database, table, column.
        UPDATE_ACL = (1U << 2), //UPDATE  Enable use of UPDATE. Levels: Global, database, table, column.
        DELETE_ACL = (1U << 3), //DELETE  Enable use of DELETE. Level: Global, database, table.
        CREATE_ACL = (1U << 4), //CREATE  Enable database and table creation. Levels: Global, database, table.
        DROP_ACL = (1U << 5),
        //DROP    Enable databases, tables, and views to be dropped. Levels: Global, database, table.
        RELOAD_ACL = (1U << 6), //RELOAD  Enable use of FLUSH operations. Level: Global.
        SHUTDOWN_ACL = (1U << 7), //SHUTDOWN    Enable use of mysqladmin shutdown. Level: Global.
        PROCESS_ACL = (1U << 8), //PROCESS Enable the user to see all processes with SHOW PROCESSLIST. Level: Global.
        FILE_ACL = (1U << 9), //FILE    Enable the user to cause the server to read or write files. Level: Global.
        GRANT_ACL = (1U << 10),
        //GRANT OPTION    Enable privileges to be granted to or removed from other accounts. Levels: Global, database, table, routine, proxy.
        REFERENCES_ACL = (1U << 11), //REFERENCES  Enable foreign key creation. Levels: Global, database, table, column.
        INDEX_ACL = (1U << 12), //INDEX   Enable indexes to be created or dropped. Levels: Global, database, table.
        ALTER_ACL = (1U << 13), //ALTER Enable use of ALTER TABLE. Levels: Global, database, table.
        SHOW_DB_ACL = (1U << 14), //SHOW DATABASES  Enable SHOW DATABASES to show all databases. Level: Global.
        SUPER_ACL = (1U << 15),
        //SUPER   Enable use of other administrative operations such as CHANGE MASTER TO, KILL, PURGE BINARY LOGS, SET GLOBAL, and mysqladmin debug command. Level: Global.
        CREATE_TMP_ACL = (1U << 16),
        //CREATE TEMPORARY TABLES Enable use of CREATE TEMPORARY TABLE. Levels: Global, database.
        LOCK_TABLES_ACL = (1U << 17),
        //LOCK TABLES Enable use of LOCK TABLES on tables for which you have the SELECT privilege. Levels: Global, database.
        EXECUTE_ACL = (1U << 18),
        //EXECUTE Enable the user to execute stored routines. Levels: Global, database, routine.
        REPL_SLAVE_ACL = (1U << 19),
        //REPLICATION SLAVE   Enable replicas to read binary log events from the source. Level: Global.
        REPL_CLIENT_ACL = (1U << 20),
        //REPLICATION CLIENT  Enable the user to ask where source or replica servers are. Level: Global.
        CREATE_VIEW_ACL = (1U << 21),
        //CREATE VIEW Enable views to be created or altered. Levels: Global, database, table.
        SHOW_VIEW_ACL = (1U << 22), //SHOW VIEW   Enable use of SHOW CREATE VIEW. Levels: Global, database, table.
        CREATE_PROC_ACL = (1U << 23), //CREATE ROUTINE  Enable stored routine creation. Levels: Global, database.
        ALTER_PROC_ACL = (1U << 24),
        //ALTER ROUTINE Enable stored routines to be altered or dropped. Levels: Global, database, routine.
        CREATE_USER_ACL = (1U << 25),
        //CREATE USER Enable use of CREATE USER, DROP USER, RENAME USER, and REVOKE ALL PRIVILEGES. Level: Global.
        EVENT_ACL = (1U << 26), //EVENT   Enable use of events for the Event Scheduler. Levels: Global, database.
        TRIGGER_ACL = (1U << 27), //TRIGGER Enable trigger operations. Levels: Global, database, table.
        CREATE_TABLESPACE_ACL = (1U << 28),
        //CREATE TABLESPACE   Enable tablespaces and log file groups to be created, altered, or dropped. Level: Global.
        PROXY_ACL = (1U << 29), //PROXY   Enable user proxying. Level: From user to user.
        NO_ACCESS_ACL = (1U << 30), //USAGE   Synonym for “no privileges”
        ALL_ACL = (1U << 31),
    };

    static const uint32_t DB_ACLS =
    (UPDATE_ACL | SELECT_ACL | INSERT_ACL | DELETE_ACL | CREATE_ACL | DROP_ACL |
     REFERENCES_ACL | INDEX_ACL | ALTER_ACL | CREATE_TMP_ACL |
     LOCK_TABLES_ACL | EXECUTE_ACL | CREATE_VIEW_ACL | SHOW_VIEW_ACL |
     CREATE_PROC_ACL | ALTER_PROC_ACL | EVENT_ACL | TRIGGER_ACL);

    static const uint32_t TABLE_ACLS =
    (SELECT_ACL | INSERT_ACL | UPDATE_ACL | DELETE_ACL | CREATE_ACL | DROP_ACL |
     REFERENCES_ACL | INDEX_ACL | ALTER_ACL | CREATE_VIEW_ACL |
     SHOW_VIEW_ACL | TRIGGER_ACL);

    static const uint32_t GLOBAL_ACLS =
    (SELECT_ACL | INSERT_ACL | UPDATE_ACL | DELETE_ACL | CREATE_ACL | DROP_ACL |
     RELOAD_ACL | SHUTDOWN_ACL | PROCESS_ACL | FILE_ACL |
     REFERENCES_ACL | INDEX_ACL | ALTER_ACL | SHOW_DB_ACL | SUPER_ACL |
     CREATE_TMP_ACL | LOCK_TABLES_ACL | REPL_SLAVE_ACL | REPL_CLIENT_ACL |
     EXECUTE_ACL | CREATE_VIEW_ACL | SHOW_VIEW_ACL | CREATE_PROC_ACL |
     ALTER_PROC_ACL | CREATE_USER_ACL | EVENT_ACL | TRIGGER_ACL |
     CREATE_TABLESPACE_ACL);

    ////////////////////////////////////////////////////////////////////////////////
    /// PrivType - Represents privilege types in DCL statements.
    ///
    /// This node encapsulates the type of SQL access control privileges (SqlACL)
    /// granted or revoked at various levels (global, database, table).
    ///
    /// Example usage:
    ///   GRANT SELECT ON mydb.mytable TO 'user';
    ///
    ///   Here, the 'type' field indicates the specific privileges granted,
    ///   such as SELECT, INSERT, etc.
    ///
    /// Notes:
    ///   - The get_priv_acl() function normalizes privileges based on grant level.
    ///   - If 'type' is ALL_ACL, it maps to corresponding predefined ACL masks
    ///     depending on the GrantLevelType.
    ////////////////////////////////////////////////////////////////////////////////

    struct PrivType : public DclNode {
        PrivType() : DclNode(pb::ast::ENT_PRIV_TYPE) {
        }

        nlohmann::ordered_json to_json() const override;

        SqlACL type;

        /// Returns normalized privilege ACL mask depending on grant level.
        /// If 'type' is ALL_ACL, returns the predefined mask for global, db, or table level.
        uint32_t get_priv_acl(pb::ast::GrantLevelType priv_level) {
            uint32_t acl = type;
            if (acl == ALL_ACL) {
                switch (priv_level) {
                    case pb::ast::GRANT_LEVEL_GLOBAL:
                        acl = GLOBAL_ACLS;
                        break;
                    case pb::ast::GRANT_LEVEL_DB:
                        acl = DB_ACLS;
                        break;
                    case pb::ast::GRANT_LEVEL_TABLE:
                        acl = TABLE_ACLS;
                        break;
                }
            }
            return acl;
        }
    };

    ////////////////////////////////////////////////////////////////////////////////
    /// PrivLevel - Represents the privilege scope in DCL statements.
    ///
    /// This node specifies the granularity level at which privileges are granted or revoked.
    /// It supports three levels of privilege scope:
    /// - Global level: applies to all databases and tables.
    /// - Database level: applies to all tables within a specified database.
    /// - Table level: applies to a specific table within a database.
    ///
    /// Fields:
    ///   - level: The grant level type, e.g., global, database, or table.
    ///   - db_name: The database name for database/table level grants. Empty if global.
    ///   - table_name: The table name for table-level grants. Empty otherwise.
    ///
    /// Example usage:
    ///   GRANT SELECT ON mydb.mytable TO 'user';  // table level
    ///   GRANT ALL ON mydb.* TO 'user';           // database level
    ///   GRANT ALL ON *.* TO 'user';               // global level
    ////////////////////////////////////////////////////////////////////////////////

    struct PrivLevel : public DclNode {
        PrivLevel() : DclNode(pb::ast::ENT_PRIV_LEVEL) {
            db_name = nullptr;
            table_name = nullptr;
        }

        nlohmann::ordered_json to_json() const override;

        /// The privilege level granularity.
        pb::ast::GrantLevelType level;

        /// Database name, used when level is database or table.
        String db_name;

        /// Table name, used only when level is table.
        String table_name;
    };
} // namespace cantor::parser
